#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <stdlib.h>

#define FPS 15
#define ECHO_SAMPLING 15
#define forn(i,n) for(i=0;i<n;i++)

extern void asmRoberts(const char* src, char* dst, int ancho, int alto, int xorder, int yorder);
extern void asmSobel(const char* src, char* dst, int ancho, int alto, int xorder, int yorder);
extern void asmPrewitt(const char* src, char* dst, int ancho, int alto, int xorder, int yorder);
extern void asmWTF(const char* src, char* dst, int ancho, int alto);
extern void asmMono(const char* src, char* dst, int ancho, int alto);

extern void merge(const char* src1, const char* src2, char* dst, int ancho, int alto);
extern void suma(const char* src1, const char* src2, char* dst, int ancho, int alto);
extern void andFilter(const char* src1, const char* src2, char* dst, int ancho, int alto);
extern void nandFilter(const char* src1, const char* src2, char* dst, int ancho, int alto);

int compare(const void* a, const void *b){
	const unsigned char* da = (const unsigned char*) a;
	const unsigned char* db = (const unsigned char*) b;
	return (*da > *db) - (*da < *db);
}

void meanBlur(const char* src, char* dst, int w, int h){
	int i; int j;
	for(i=2;i<h-2;i++) for(j=2;j<w-2;j++){
			unsigned int aux = 0;
			int k, l;
			for(k=-2;k<3;k++) for(l=-2;l<3;l++){
				aux += (unsigned char)src[(i+k)*w + j+l];
			}
			dst[i*w + j] = (unsigned char)(aux/25);
	}
}

int main( int argc, char** argv ){
	
	int pscreen=0;
   IplImage * src = 0;
   IplImage * dst = 0;
   IplImage * dst_ini = 0;

   char* filename = argc==3?argv[2]:(char*)"lena.bmp";

	if(argc>=2){
		if(strcmp(argv[1],"webcam")){
			if((src = cvLoadImage(filename, CV_LOAD_IMAGE_GRAYSCALE))==0) return -1;
			if((dst = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1))==0) return -1;
		}
		
		if(!strcmp(argv[1],"r1")){
			asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 0);
			cvSaveImage("sobel_x.bmp", dst);
			printf("%s\n","output written to sobel_x.bmp");
			
		}else if(!strcmp(argv[1],"r2")){
			asmSobel(src->imageData, dst->imageData, src->width, src->height, 0, 1);
			cvSaveImage("sobel_y.bmp", dst);
			printf("%s\n","output written to sobel_y.bmp");
			
		}else if(!strcmp(argv[1],"r3")){
			asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 1);
			cvSaveImage("sobel.bmp", dst);
			printf("%s\n","output written to sobel.bmp");
		
		}else if(!strcmp(argv[1],"prewitt")){
			asmPrewitt(src->imageData, dst->imageData, src->width, src->height, 1, 1);
			cvSaveImage("prewitt.bmp", dst);
			printf("%s\n","output written to prewitt.bmp");
		
		}else if(!strcmp(argv[1],"roberts")){
			asmRoberts(src->imageData, dst->imageData, src->width, src->height, 1, 1);
			cvSaveImage("roberts.jpg", dst);
			printf("%s\n","output written to roberts.jpg");
			
		}else if(!strcmp(argv[1],"wtf")){
			asmWTF(src->imageData, dst->imageData, src->width, src->height);
			cvSaveImage("wtf.jpg", dst);
			printf("%s\n","output written to wtf.jpg");
		
		}else if(!strcmp(argv[1],"mono")){
			asmMono(src->imageData, dst->imageData, src->width, src->height);
			cvSaveImage("mono.jpg", dst);
			printf("%s\n","output written to mono.jpg");
			
		}else if(!strcmp(argv[1],"time")){

			int tscl;
			long long int c;
			int i;

			c = 0;
			forn(i,1000){
				// Tomar estado del TSC antes de iniciar el procesamiento de bordes.
				__asm__ __volatile__ ("rdtsc;mov %%eax,%0" : : "g" (tscl));

				asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 0);

				// Tomo la medición de tiempo con el TSC y calculo la diferencia. Resultado:
				// Cantidad de clocks insumidos por el algoritmo.
				__asm__ __volatile__ ("rdtsc;sub %0,%%eax;mov %%eax,%0" : : "g" (tscl));
				c += tscl;
			}
			cvSaveImage("sobel_x.bmp", dst);
			printf("%s\n","output written to sobel_x.bmp");
			printf("ciclos casero: %f\n",(double)c/1000);

			/* Ahora la lib openCv */

			c = 0;
			forn(i,1000){
				// Tomar estado del TSC antes de iniciar el procesamiento de bordes.
				__asm__ __volatile__ ("rdtsc;mov %%eax,%0" : : "g" (tscl));

				cvSobel(src, dst, 1, 0, 3);

				// Tomo la medición de tiempo con el TSC y calculo la diferencia. Resultado:
				// Cantidad de clocks insumidos por el algoritmo.
				__asm__ __volatile__ ("rdtsc;sub %0,%%eax;mov %%eax,%0" : : "g" (tscl));
				c += tscl;
			}
			/* Imprimo los resultados */

			cvSaveImage("sobel_openCv_x.bmp", dst);
			printf("%s\n","output written to sobel_openCv_x.bmp");
			printf("ciclos openCv: %f\n",(double)c/1000);


		}else if(!strcmp(argv[1],"blur")){
			int i; int j; int h; int w;
			h = src->height; w = src->width;
			for(i=1;i<h-1;i++) for(j=1;j<w-1;j++){
					unsigned char aux[9];
					aux[0] = (src->imageData)[(i-1)*w + j-1];
					aux[1] = (src->imageData)[(i-1)*w + j];
					aux[2] = (src->imageData)[(i-1)*w + j+1];
					aux[3] = (src->imageData)[i*w + j-1];
					aux[4] = (src->imageData)[i*w + j];
					aux[5] = (src->imageData)[i*w + j+1];
					aux[6] = (src->imageData)[(i+1)*w + j-1];
					aux[7] = (src->imageData)[(i+1)*w + j];
					aux[8] = (src->imageData)[(i+1)*w + j+1];
					qsort(aux,8, sizeof(unsigned char),compare);
					(dst->imageData)[i*w + j] = aux[4];
			}
			cvSaveImage("blur.jpg", dst);
			printf("%s\n","output written to blur.jpg");
		}else if(!strcmp(argv[1],"blur_mean")){
			int i; int j; int h; int w;
			h = src->height; w = src->width;
			for(i=1;i<h-1;i++) for(j=1;j<w-1;j++){
					unsigned int aux = 0;
					aux += (unsigned char)(src->imageData)[(i-1)*w + j-1];
					aux += (unsigned char)(src->imageData)[(i-1)*w + j];
					aux += (unsigned char)(src->imageData)[(i-1)*w + j+1];
					aux += (unsigned char)(src->imageData)[i*w + j-1];
					aux += (unsigned char)(src->imageData)[i*w + j];
					aux += (unsigned char)(src->imageData)[i*w + j+1];
					aux += (unsigned char)(src->imageData)[(i+1)*w + j-1];
					aux += (unsigned char)(src->imageData)[(i+1)*w + j];
					aux += (unsigned char)(src->imageData)[(i+1)*w + j+1];
					(dst->imageData)[i*w + j] = (unsigned char)(aux/9);
			}
			cvSaveImage("blur_mean.jpg", dst);
			printf("%s\n","output written to blur_mean.jpg");
		}else if(!strcmp(argv[1],"webcam")){
			CvCapture* capture = NULL;
			IplImage* frame = NULL;
			cvNamedWindow("camara",1);
			int state = 0;
			
			IplImage* e[ECHO_SAMPLING];
			int echo_index = 0;
			
			// create capture
			capture = (argc==3)?cvCaptureFromAVI(filename):cvCaptureFromCAM(-1);
			if(!capture){
				printf("%s\n","Error al conectar la camara");
				return -1;
			}
			frame = cvQueryFrame(capture);
			if((src = cvCreateImage(cvSize(frame->width,frame->height), IPL_DEPTH_8U, 1))==0) return -1;
			if((dst = cvCreateImage(cvSize(frame->width,frame->height), IPL_DEPTH_8U, 1))==0) return -1;
			
			IplImage* aux;
			if((aux = cvCreateImage(cvSize(frame->width,frame->height), IPL_DEPTH_8U, 1))==0) return -1;

			int i;
			for(i=0;i<ECHO_SAMPLING;i++) if((e[i] = cvCreateImage(cvSize(frame->width,frame->height), IPL_DEPTH_8U, 1))==0) return -1;

			cvMoveWindow("camara",100,100);

			while(1){
				frame = cvQueryFrame(capture);
				if(state==0){
					cvCvtColor(frame,dst,CV_BGR2GRAY); 										// convierte RGB a Grayscale
				}else if(state==1){
					cvCvtColor(frame,src,CV_BGR2GRAY); // convierte RGB a Grayscale
					asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 0); // aplico la mascara de sobel en X
					//meanBlur(src->imageData,dst->imageData,src->width,src->height);
				}else if(state==2){
					cvCvtColor(frame,src,CV_BGR2GRAY); // convierte RGB a Grayscale
					asmSobel(src->imageData, dst->imageData, src->width, src->height, 0, 1); // aplico la mascara de sobel en Y
				}else if(state==3){
					cvCvtColor(frame,src,CV_BGR2GRAY); // convierte RGB a Grayscale
					asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 1); // aplico la mascara de sobel en X e Y
				}else if(state==4){
					cvCvtColor(frame,src,CV_BGR2GRAY); // convierte RGB a Grayscale
					asmWTF(src->imageData, dst->imageData, src->width, src->height); // aplico la mascara WTF
				}else if(state==5){
					cvCvtColor(frame,src,CV_BGR2GRAY); // convierte RGB a Grayscale
					asmPrewitt(src->imageData, dst->imageData, src->width, src->height, 1, 1); // aplico la mascara Prewitt
				}else if(state==6){
					cvCvtColor(frame,src,CV_BGR2GRAY); // convierte RGB a Grayscale
					asmMono(src->imageData, dst->imageData, src->width, src->height); // aplico la mascara Monocromo
				}else if(state==7){
					cvCvtColor(frame,src,CV_BGR2GRAY); // convierte RGB a Grayscale
					asmRoberts(src->imageData, dst->imageData, src->width, src->height, 1, 1); // aplico la mascara Roberts
				}else if(state==8){
					cvCvtColor(frame,e[echo_index],CV_BGR2GRAY); // convierte RGB a Grayscale
					//asmRoberts(e[(echo_index+1)%ECHO_SAMPLING]->imageData,src->imageData,src->width, src->height, 1, 1);
					//asmSobel(e[(echo_index+(ECHO_SAMPLING/2))%ECHO_SAMPLING]->imageData,e[(echo_index+1)%ECHO_SAMPLING]->imageData,aux->width, aux->height, 1, 1);
					//merge( e[(echo_index+1)%ECHO_SAMPLING]->imageData , src->imageData, aux->imageData, dst->width, dst->height);
					//merge( e[echo_index]->imageData , aux->imageData, dst->imageData, dst->width, dst->height);
					asmSobel(e[(echo_index+1)%ECHO_SAMPLING]->imageData,aux->imageData,aux->width, aux->height, 1, 1);
					merge( e[echo_index]->imageData , aux->imageData, dst->imageData, dst->width, dst->height);
					echo_index++;
					echo_index %= ECHO_SAMPLING;
				}else if(state==9){
					cvCvtColor(frame,src,CV_BGR2GRAY); // convierte RGB a Grayscale
					IplImage* dst1;
					IplImage* dst2;
					if((dst1 = cvCreateImage(cvSize(frame->width,frame->height), IPL_DEPTH_8U, 1))==0) return -1;
					if((dst2 = cvCreateImage(cvSize(frame->width,frame->height), IPL_DEPTH_8U, 1))==0) return -1;
					if((aux = cvLoadImage((char*)"Untitled.bmp", CV_LOAD_IMAGE_GRAYSCALE))==0) return -1;
					asmSobel(src->imageData,dst->imageData,aux->width, aux->height,1,1);
					andFilter(src->imageData,aux->imageData,dst1->imageData, aux->height, aux->width);
					nandFilter(dst->imageData,aux->imageData,dst2->imageData, aux->height, aux->width);
					suma( dst1->imageData , dst2->imageData, dst->imageData, dst->width, dst->height);
				}
				cvShowImage("camara",dst);
				int key = cvWaitKey(1000/FPS);
				if((char)key==27){
					break;
				}else if( '0'<=(char)key && (char)key<='9' ){
					state = atoi((char*)&key);
					//printf("%d\n",state);
				}else if((char)key=='p'){
					char psname[200];
					char* ss="screenshot";
					char* jpg=".jpg";
					sprintf(psname,"%s%d%s",ss,pscreen,jpg);
					pscreen++;
					cvSaveImage(psname, dst);
					printf("%s\n","output written to printScreen.jpg");
				}
			}
			cvReleaseCapture(&capture);
			cvDestroyWindow("camara");
		}
	}
   return 0;
}


